package com.tancire.net.service.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson2.JSONObject;
import com.tancire.common.constant.BnsConst;
import com.tancire.common.core.domain.entity.SysUser;
import com.tancire.common.core.domain.model.LoginUser;
import com.tancire.common.core.redis.RedisCache;
import com.tancire.common.exception.net.NetActivityException;
import com.tancire.common.utils.DateUtils;
import com.tancire.common.utils.SecurityUtils;
import com.tancire.common.utils.StringUtils;
import com.tancire.common.utils.uuid.IdUtils;
import com.tancire.net.domain.*;
import com.tancire.net.enums.SourceAuthTypeEnum;
import com.tancire.net.service.IAuthLogService;
import com.tancire.net.service.INetUserService;
import com.tancire.net.service.IPurseInfoService;
import org.springframework.stereotype.Service;
import com.tancire.net.mapper.AuthInfoMapper;
import com.tancire.net.service.IAuthInfoService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 认证应用（资源）信息Service业务层处理
 *
 * @author chenchao
 * @date 2023-11-18
 */
@Service
public class AuthInfoServiceImpl implements IAuthInfoService {

    @Resource
    private AuthInfoMapper authInfoMapper;

    @Resource
    private RedisCache redisCache;

    @Resource
    private INetUserService netUserService;

    @Resource
    private IPurseInfoService purseInfoService;

    @Resource
    private IAuthLogService authLogService;

    /**
     * 查询认证应用（资源）信息
     *
     * @param id 认证应用（资源）信息主键
     * @return 认证应用（资源）信息
     */
    @Override
    public AuthInfo selectAuthInfoById(Long id) {
        return authInfoMapper.selectAuthInfoById(id);
    }

    /**
     * 查询认证应用（资源）信息列表
     *
     * @param authInfo 认证应用（资源）信息
     * @return 认证应用（资源）信息
     */
    @Override
    public List<AuthInfo> selectAuthInfoList(AuthInfo authInfo) {
        return authInfoMapper.selectAuthInfoList(authInfo);
    }

    /**
     * 新增认证应用（资源）信息
     *
     * @param authInfo 认证应用（资源）信息
     * @return 结果
     */
    @Override
    public int insertAuthInfo(AuthInfo authInfo) {
        authInfo.setCreateTime(DateUtils.getNowDate());
        Integer sourceAuthTime = authInfo.getSourceAuthTime();
        if (!SourceAuthTypeEnum.SIMPLE.getCode().equals(authInfo.getSourceAuthType())) {
            if (null == sourceAuthTime) {
                throw new NetActivityException("授权码超时时间不能为空");
            }
        }
        // 判断认证类型
        if (SourceAuthTypeEnum.SIMPLE.getCode().equals(authInfo.getSourceAuthType())) {
            // 简单授权，校验一下信息
            String sourcePassword = authInfo.getSourcePassword();
            if (StringUtils.isBlank(sourcePassword)) {
                throw new NetActivityException("简单授权密钥不能为空");
            }
        } else if (SourceAuthTypeEnum.CALL_SIMPLE.getCode().equals(authInfo.getSourceAuthType())) {
            // 调用校验授权
        } else if (SourceAuthTypeEnum.ENCRYPT.getCode().equals(authInfo.getSourceAuthType())) {
            // 加密授权 生成私钥和公钥，用于生成加密信息
            // 创建密钥
            RSA rsa = new RSA();
            //获得私钥
            String privateKey = rsa.getPrivateKeyBase64();
            //获得公钥
            String publicKey = rsa.getPublicKeyBase64();
            // 保存公钥和私钥
            authInfo.setSourcePrivateKey(privateKey);
            authInfo.setSourcePublicKey(publicKey);
        } else if (SourceAuthTypeEnum.CALL_ENCRYPT.getCode().equals(authInfo.getSourceAuthType())) {
            // 加密调用校验授权
            // 创建密钥
            RSA rsa = new RSA();
            //获得私钥
            String privateKey = rsa.getPrivateKeyBase64();
            //获得公钥
            String publicKey = rsa.getPublicKeyBase64();
            // 保存公钥和私钥
            authInfo.setSourcePrivateKey(privateKey);
            authInfo.setSourcePublicKey(publicKey);
        }
        return authInfoMapper.insertAuthInfo(authInfo);
    }

    /**
     * 修改认证应用（资源）信息
     *
     * @param authInfo 认证应用（资源）信息
     * @return 结果
     */
    @Override
    public int updateAuthInfo(AuthInfo authInfo) {
        authInfo.setUpdateTime(DateUtils.getNowDate());
        return authInfoMapper.updateAuthInfo(authInfo);
    }

    /**
     * 批量删除认证应用（资源）信息
     *
     * @param ids 需要删除的认证应用（资源）信息主键
     * @return 结果
     */
    @Override
    public int deleteAuthInfoByIds(Long[] ids) {
        return authInfoMapper.deleteAuthInfoByIds(ids);
    }

    /**
     * 删除认证应用（资源）信息信息
     *
     * @param id 认证应用（资源）信息主键
     * @return 结果
     */
    @Override
    public int deleteAuthInfoById(Long id) {
        return authInfoMapper.deleteAuthInfoById(id);
    }

    /**
     * 根据授权码获取资源激活码
     *
     * @param authDetail
     * @return
     */
    @Transactional
    @Override
    public AuthDetail authByCode(AuthDetail authDetail, boolean deduct) {
        String sourceCode = authDetail.getSourceCode();
        if (StringUtils.isBlank(sourceCode)) {
            throw new NetActivityException("机器码不能为空");
        }
        // 查询信息
        AuthInfo query = new AuthInfo();
        query.setSourceCode(sourceCode);
        List<AuthInfo> authInfoList = this.authInfoMapper.selectAuthInfoList(query);
        if (ObjectUtil.isEmpty(authInfoList)) {
            throw new NetActivityException("未查询到授权信息");
        }
        AuthInfo authInfo = authInfoList.get(0);
        // 校验及扣积分
        LoginUser loginUser = SecurityUtils.getLoginUser();
        NetUser userInfo = netUserService.getUserInfoById(loginUser.getUserId());
        if (null == userInfo) {
            throw new NetActivityException("查询账号信息失败");
        }
        // 扣积分逻辑
        BigDecimal sourceAuthFee = authInfo.getSourceAuthFee();
        if (deduct && null != sourceAuthFee && sourceAuthFee.compareTo(BigDecimal.ZERO) > 0) {
            // 执行扣积分逻辑
            this.useUserPoints(sourceAuthFee);
        }
        // 初始化返回信息
        AuthDetail res = new AuthDetail();
        res.setSourceCode(sourceCode);
        res.setSourceName(authInfo.getSourceName());
        // 判断不同的授权逻辑走不同的规则
        if (SourceAuthTypeEnum.SIMPLE.getCode().equals(authInfo.getSourceAuthType())) {
            // 简单授权，校验一下信息
            res.setSecret(authInfo.getSourcePassword());
        } else if (SourceAuthTypeEnum.CALL_SIMPLE.getCode().equals(authInfo.getSourceAuthType())) {
            // 调用校验授权
            // 生成随机码，缓存redis
            String secret = IdUtils.fastSimpleUUID();
            // 缓存redis 5分钟失效
            redisCache.setCacheObject(BnsConst.CALL_SIMPLE_KEY + secret, sourceCode, 300, TimeUnit.SECONDS);
            // 返回校验码
            res.setSecret(secret);
        } else if (SourceAuthTypeEnum.ENCRYPT.getCode().equals(authInfo.getSourceAuthType())) {
            // 加密授权 生成私钥和公钥，用于生成加密信息
            RSA rsa = new RSA(authInfo.getSourcePrivateKey(), null);
            // 构建校验实体
            AuthBean authBean = new AuthBean();
            authBean.setCode(authInfo.getSourceCode());
            // 加入认证账号
            SysUser user = loginUser.getUser();
            NetUser netUser = this.netUserService.selectNetUserById(user.getUserId());
            authBean.setAccount(netUser.getAccount());
            Date current = new Date();
            long time = current.getTime();
            Integer sourceAuthTime = authInfo.getSourceAuthTime();
            time = time + (sourceAuthTime * 1000);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            authBean.setExpireTime(sdf.format(new Date(time)));
            // 加密数据
            String secretStr = JSONObject.toJSONString(authBean);
            byte[] encrypt = rsa.encrypt(StrUtil.bytes(secretStr, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
            String secret = Base64.getEncoder().encodeToString(encrypt);
            // 返回激活码
            res.setSecret(secret);
        } else if (SourceAuthTypeEnum.CALL_ENCRYPT.getCode().equals(authInfo.getSourceAuthType())) {
            // 加密调用校验授权
            // 暂时废弃，感觉没啥用
            throw new NetActivityException("暂不支持当前模式");
        } else {
            throw new NetActivityException("数据异常，请联系客服");
        }
        this.saveAuthLog(authInfo, "检索获取激活码");
        return res;
    }

    /**
     * 保存认证日志
     *
     * @param authInfo
     */
    private void saveAuthLog(AuthInfo authInfo, String remark) {
        AuthLog authLog = new AuthLog();
        authLog.setAuthId(authInfo.getId());
        authLog.setAuthCode(authInfo.getSourceCode());
        authLog.setAuthPrice(authInfo.getSourceAuthFee());
        // 保存操作信息
        SysUser user = SecurityUtils.getLoginUser().getUser();
        authLog.setUserId(user.getUserId());
        authLog.setUserName(user.getNickName());
        authLog.setCreateByName(user.getNickName());
        authLog.setCreateBy(String.valueOf(user.getUserId()));
        authLog.setCreateTime(new Date());
        authLog.setSysFlag(user.getUserSource());
        // 日志
        authLog.setLogRemark(remark);
        authLogService.insertAuthLog(authLog);
    }

    /**
     * 扣除积分，校验等
     *
     * @param sourceAuthFee
     */
    private void useUserPoints(BigDecimal sourceAuthFee) {
        if (null == sourceAuthFee) {
            return;
        }
        SysUser user = SecurityUtils.getLoginUser().getUser();
        // 查询钱包余额
        PurseInfo purseInfo = purseInfoService.selectPurseInfoByUserId(user.getUserId());
        if (null != purseInfo) {
            BigDecimal activePoints = purseInfo.getActivePoints();
            if (null != activePoints && activePoints.compareTo(sourceAuthFee) >= 0) {
                purseInfoService.subPoints(user.getUserId(), sourceAuthFee, "查询激活码消耗积分");
            }
        } else {
            throw new NetActivityException("会员积分不足");
        }
    }

    /**
     * 根据资源编码查询资源基础信息
     *
     * @param code
     * @return
     */
    @Override
    public AuthInfo selectAuthInfoByCode(String code) {
        return this.authInfoMapper.selectAuthInfoByCode(code);
    }

    /**
     * 查询平台应用列表
     *
     * @param authInfo
     * @return
     */
    @Override
    public List<AuthInfo> selectAuthInfoListAll(AuthInfo authInfo) {
        // 进查询平台资源
        authInfo.setSourceBelong(0);
        List<AuthInfo> authInfolist = authInfoMapper.selectAuthInfoList(authInfo);
        return authInfolist;
    }
}
